package com.virgo.web.utils;

import com.virgo.common.exception.IApplicationException;
import com.virgo.common.utils.ICommonUtils;
import com.virgo.common.utils.ITimeUtils;
import com.virgo.web.config.SystemConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;

@Component
public class IFileUtils {

	@Autowired
	SystemConfig systemConfig;

	/**
	 * 上传普通文件
	 * @param dir 上传目录
	 * @param file 文件对象
	 * @return 文件路径
	 */
	public String upload(String dir, MultipartFile file) {
		if (file == null || file.isEmpty()) {
			throw new IApplicationException("请选择上传的文件!");
		}
		try {
			String suffix = getFileType(file.getInputStream());
			if ("".equalsIgnoreCase(suffix)) {
				throw new IApplicationException("不支持的文件类型!");
			}
			String OriginalFilename = file.getOriginalFilename();
			if (ObjectUtils.isEmpty(OriginalFilename)) {
				throw new IApplicationException("文件错误");
			}
			String baseRoot = systemConfig.getUploadPath();
			String year = ITimeUtils.localDate("yyyy");
			String month = ITimeUtils.localDate("MM");
			String day = ITimeUtils.localDate("dd");
			String uploadPath = baseRoot + "/"  + dir + "/" + year + "/" + month + "/" + day;
			createDir(uploadPath);
			int position = OriginalFilename.lastIndexOf(".");
			position += 1;
			suffix = OriginalFilename.substring(position).toLowerCase();
			String fileName = uploadPath + "/" + getRandomFileName() + "." + suffix.toLowerCase();
			File newFile = new File(fileName);
			file.transferTo(newFile); //保存文件
			return fileName.replace(baseRoot, "");
		} catch (Exception e) {
			throw new IApplicationException("文件上传错误");
		}
	}

	/**
	 * base64的文件上传
	 * @param dir 目录
	 * @param base64Data 文件数据
	 * @return 文件路径
	 */
	public String uploadBase64(String dir, String base64Data) {
		try {
			String dataPrix = "";
			String data = "";
			if (ObjectUtils.isEmpty(base64Data)) {
				throw new IApplicationException("上传失败，上传图片数据为空");
			} else {
				String[] d = base64Data.split("base64,");
				if (d.length == 2) {
					dataPrix = d[0];
					data = d[1];
				} else {
					throw new IApplicationException("上传失败，数据不合法");
				}
			}

			String suffix = "";
			if ("data:image/jpeg;".equalsIgnoreCase(dataPrix)) {//编码的jpeg图片数据
				suffix = ".jpg";
			} else if ("data:image/x-icon;".equalsIgnoreCase(dataPrix)) {//编码的icon图片数据
				suffix = ".ico";
			} else if ("data:image/gif;".equalsIgnoreCase(dataPrix)) {//编码的gif图片数据
				suffix = ".gif";
			} else if ("data:image/png;".equalsIgnoreCase(dataPrix)) {//编码的png图片数据
				suffix = ".png";
			} else {
				throw new IApplicationException("上传图片格式不合法");
			}

			String webRoot = systemConfig.getUploadPath();
			String year = ITimeUtils.localDate("yyyy");
			String month = ITimeUtils.localDate("MM");
			String day = ITimeUtils.localDate("dd");
			String uploadPath = webRoot + "/"  + dir + "/" + year + "/" + month + "/" + day;
			createDir(uploadPath);

			String fileName = uploadPath + "/" + getRandomFileName() + suffix;
			writeByteArrayToFile(fileName, Base64.getDecoder().decode(data));
			return fileName.replace(webRoot, "");
		} catch (Exception ignored) {
			throw new IApplicationException("文件上传错误");
		}
	}

	/**
	 * 上传文件分片
	 * @param file MultipartFile
	 * @param uuid uuid
	 * @param index index
	 */
	public void uploadFileChunk(MultipartFile file, String uuid, String index) {
		String webRoot = systemConfig.getUploadPath();
		String uploadPath = webRoot + "/bigfile/_temp/" + uuid;
		createDir(uploadPath);
		String uploadFilePath = uploadPath + "/" + index;
		try {
			saveFile(file, uploadFilePath);
		} catch (Exception e) {
			throw new IApplicationException("文件上传失败");
		}
	}

	/**
	 * 合并文件
	 * @param uuid String
	 * @param ext String
	 * @return String
	 */
	public String mergeFileChunks(String uuid, String ext) {
		String webRoot = systemConfig.getUploadPath();
		String chunkPath = webRoot + "/bigfile/_temp/" + uuid;
		String year = ITimeUtils.localDate("yyyy");
		String month = ITimeUtils.localDate("MM");
		String day = ITimeUtils.localDate("dd");
		String uploadPath = webRoot + "/bigfile/" + year + "/" + month + "/" + day;
		createDir(uploadPath);

		String savePath = uploadPath + "/" + getRandomFileName() + "." + ext;

		try {
			// 合并切片文件
			mergeFiles(chunkPath,savePath);

			// 删除上传的切片文件
			deleteFileChunks(chunkPath);

			// 返回成功响应
			return savePath.replace(webRoot, "");
		} catch (Exception e) {
			return "";
		}
	}

	/**
	 * 读取文件信息到string
	 * @param fileName 文件路径
	 * @return 文件内容字符串
	 */
	public String readFileToString(String fileName) {
		String encoding = "UTF-8";
		File file = new File(fileName);
		long fileLength = file.length();
		byte[] fileContent = new byte[(int) fileLength];
		try {
			FileInputStream in = new FileInputStream(file);
			int contentLength = in.read(fileContent);
			if (contentLength <= 0) {
				throw new IApplicationException("文件内容为空");
			}
			in.close();
		} catch (IOException e) {
			throw new IApplicationException("文件读取失败");
		}
		try {
			return new String(fileContent, encoding);
		} catch (UnsupportedEncodingException e) {
			throw new IApplicationException("获取文件内容失败");
		}
	}

	/**
	 * byte转hex
	 * @param src 字节数组
	 * @return 字符串
	 */
	public String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder();
		if (src == null || src.length <= 0) {
			return "";
		}
		for (byte b : src) {
			int v = b & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}

	/**
	 * 获取文件类型
	 * @param inputStream 文件对象
	 * @return 文件类型
	 */
	public String getFileType(InputStream inputStream) {
		try {
			byte[] b = new byte[3];
			int contentLength = inputStream.read(b, 0, b.length);
			if (contentLength == 0) {
				throw new IApplicationException("文件内容为空");
			}
			String fileTag = bytesToHexString(b);
			fileTag = fileTag.toUpperCase();
			switch (fileTag) {
				case "FFD8FF":
					return "jpg";
				case  "000516":
					return "jpg";
				case "89504E":
					return "png";
				case "474946":
					return "gif";
				case "D0CF11":
					return "doc";
				case "504B03":
					return "docx";
				case "255044":
					return "pdf";
				case "377ABC":
					return "7z";
				case "526172":
					return "rar";
				case "000000":
					return "mp4";
			}
		} catch (Exception ignored) {
		}
		return "";
	}

	/**
	 * 将字节流转换成文件
	 * @param filename 文件路径
	 * @param data 字节流
	 * @throws Exception 异常
	 */
	public void writeByteArrayToFile(String filename, byte[] data) throws Exception {
		if (data != null) {
			File file = new File(filename);
			if (file.exists()) {
				file.delete();
			}
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(data, 0, data.length);
			fos.flush();
			fos.close();
		}
	}

	/**
	 * 创建文件夹
	 * @param dirName 路径
	 */
	public void createDir(String dirName) {
		File file = new File(dirName);
		if (!file.exists()) {
			if (file.getParentFile().exists()) {
				file.mkdir();
			} else {
				createDir(file.getParentFile().getAbsolutePath());
				file.mkdir();
			}
		}
	}

	/**
	 * 将上传文件流保存到指定路径
	 * @param file MultipartFile
	 * @param filePath String
	 * @throws IOException 异常
	 */
	private void saveFile(MultipartFile file, String filePath) throws IOException {
		File destFile = new File(filePath);
		file.transferTo(destFile);
	}

	/**
	 * 合并文件
	 * @param chunkPath String
	 * @param savePath savePath
	 * @throws IOException 异常
	 */
	private void mergeFiles(String chunkPath, String savePath) throws IOException {
		File directory = new File(chunkPath);
		File[] listFiles = directory.listFiles();
		if (ObjectUtils.isEmpty(listFiles)) {
			return;
		}
		ArrayList<Integer> nameList = new ArrayList<>();
		HashMap<Integer, File> fileMap = new HashMap<>();
		for (File file : listFiles) {
			Integer nameTemp = Integer.parseInt(file.getName());
			nameList.add(nameTemp);
			fileMap.put(nameTemp,file);
		}
		Collections.sort(nameList);
		try (OutputStream os = new FileOutputStream(savePath)) {
			for (Integer name : nameList) {
				File file = fileMap.get(name);
				Files.copy(file.toPath(), os);
			}
		}
	}

	/**
	 * 删除文件夹
	 * @param chunkPath String 路径
	 */
	public void deleteFileChunks(String chunkPath) {
		File directory = new File(chunkPath);
		File[] fileList = directory.listFiles();
		if (ObjectUtils.isEmpty(fileList)) {
			return;
		}
		for (File file : fileList) {
			file.delete();
		}
		directory.delete();
	}

	/**
	 * 获取随机数文件
	 * @return 文件名
	 */
	private String getRandomFileName() {
		String fileName = ITimeUtils.localDate("yyyyMMddHHmmss");
		fileName += ICommonUtils.getRandom(1000, 9999);
		return fileName;
	}
}